<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import draggable from "vuedraggable";
import { ElMessage, ElMessageBox } from "element-plus";
import type { ElMessage as ElMessageType } from "element-plus";
import {
  Delete,
  Upload,
  Plus,
  Document,
  Folder,
  Picture,
  Edit,
  ArrowLeft,
  ArrowRight,
  Search,
  More,
  Download
} from "@element-plus/icons-vue";
import {
  getFileTree as getFileList,
  deleteItems,
  createFolder,
  uploadFile,
  renameItem,
  sortItems,
  updateIcon,
  uploadToOSS
} from "@/api/file";
import type { FileItem } from "@/api/file";

// 文件树数据
const fileTree = ref<FileItem[]>([]);
// 当前显示的文件列表
const fileList = ref<FileItem[]>([]);
// 当前文件夹ID
const curFolderId = ref<number | undefined>(undefined);
// 搜索关键字
const searchKeyword = ref("");
// 当前路径
const currentPath = ref<string[]>(["我的文件"]);

// 查找指定文件夹
const findCurrentFolder = (
  items: FileItem[],
  folderId: number
): FileItem | null => {
  for (const item of items) {
    if (item.id === folderId) {
      return item;
    }
    if (item.children?.length) {
      const result = findCurrentFolder(item.children, folderId);
      if (result) return result;
    }
  }
  return null;
};

// 查找当前文件夹的父级
const findParentFolder = (
  items: FileItem[],
  folderId: number
): FileItem | null => {
  for (const item of items) {
    if (item.children?.length) {
      for (const child of item.children) {
        if (child.id === folderId) {
          return item;
        }
      }
      const result = findParentFolder(item.children, folderId);
      if (result) return result;
    }
  }
  return null;
};
// 获取文件图标
const getFileIcon = (mimeType: string, icon_url?: string) => {
  // 如果有自定义图标，优先使用自定义图标
  if (icon_url) {
    return icon_url;
  }
  if (mimeType === "folder") return Folder;
  if (mimeType.startsWith("image/")) return Picture;
  return Document;
};

// 创建文件夹
const createNewFolder = () => {
  ElMessageBox.prompt("请输入文件夹名称", "新建文件夹", {
    confirmButtonText: "确定",
    cancelButtonText: "取消"
  }).then(async ({ value }) => {
    try {
      const { success, data } = await createFolder({
        name: value,
        parentId: curFolderId.value
      });

      if (success && data) {
        // 更新文件树
        if (!curFolderId.value) {
          // 在根目录创建
          fileTree.value.push(data);
          fileList.value = fileTree.value;
        } else {
          // 在子文件夹中创建
          const parentFolder = findCurrentFolder(
            fileTree.value,
            curFolderId.value
          );
          if (parentFolder) {
            if (!parentFolder.children) {
              parentFolder.children = [];
            }
            parentFolder.children.push(data);
            fileList.value = parentFolder.children;
          }
        }
        ElMessage.success("创建成功");
      }
    } catch (error) {
      console.error("创建文件夹错误:", error);
      ElMessage.error("创建文件夹失败");
    }
  });
};

// 文件上传
const handleUpload = async (file: File | File[]) => {
  // 过滤掉隐藏文件
  const filterHiddenFile = (f: File) => !f.name.startsWith(".");

  const formData = new FormData();

  if (Array.isArray(file)) {
    file.filter(filterHiddenFile).forEach(f => {
      formData.append("files", f); // 直接使用原始文件
    });
  } else {
    if (!filterHiddenFile(file)) {
      return;
    }
    formData.append("files", file); // 直接使用原始文件
  }

  // 如果没有文件要上传，直接返回
  if (formData.getAll("files").length === 0) {
    return;
  }

  // 如果在文件夹内，添加父文件夹ID
  if (curFolderId.value) {
    formData.append("parentId", curFolderId.value.toString());
  }

  try {
    const { success, data } = await uploadFile(formData);
    if (success && data) {
      // 获取最新的文件树
      const { success: treeSuccess, data: treeData } = await getFileList();
      if (treeSuccess && treeData) {
        fileTree.value = treeData;

        // 更新当前显示的文件列表
        if (!curFolderId.value) {
          fileList.value = fileTree.value;
        } else {
          const currentFolder = findCurrentFolder(
            fileTree.value,
            curFolderId.value
          );
          if (currentFolder?.children) {
            fileList.value = currentFolder.children;
          }
        }

        ElMessage.success("上传成功");
      }
    }
  } catch (error) {
    console.error("上传文件错误:", error);
    ElMessage.error("上传失败");
  }
};

// 拖拽状态
const isDragging = ref(false);

// 拖拽进入处理
const handleDragEnter = (e: DragEvent) => {
  e.preventDefault();
  const items = Array.from(e.dataTransfer?.items || []);
  const hasValidItems = items.some(item => {
    const entry = item.webkitGetAsEntry?.();
    return entry && (entry.isFile || entry.isDirectory);
  });

  if (hasValidItems) {
    isDragging.value = true;
  }
};

// 拖拽离开处理
const handleDragLeave = (e: DragEvent) => {
  // 确保只在离开整个区域时才触发
  const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
  if (
    e.clientX <= rect.left ||
    e.clientX >= rect.right ||
    e.clientY <= rect.top ||
    e.clientY >= rect.bottom
  ) {
    isDragging.value = false;
  }
};

// 修改拖拽上传处理
const handleDrop = async (e: DragEvent) => {
  e.preventDefault();
  isDragging.value = false;

  // 获取所有拖拽的项目
  const items = Array.from(e.dataTransfer?.items || []);

  // 处理文件夹和文件
  for (const item of items) {
    if (item.webkitGetAsEntry) {
      const entry = item.webkitGetAsEntry();
      if (entry) {
        await processEntry(entry);
      }
    }
  }
};

// 处理文件系统条目
const processEntry = async (entry: FileSystemEntry) => {
  // 跳过隐藏文件和文件夹
  if (entry.name.startsWith(".")) {
    return;
  }

  if (entry.isFile) {
    // 处理文件
    const file = await getFileFromEntry(entry as FileSystemFileEntry);
    if (file) {
      await handleUpload(file);
    }
  } else if (entry.isDirectory) {
    // 处理文件夹
    const dirEntry = entry as FileSystemDirectoryEntry;
    await processDirectory(dirEntry);
  }
};

// 从文件条目获取文件
const getFileFromEntry = (entry: FileSystemFileEntry): Promise<File> => {
  return new Promise((resolve, reject) => {
    entry.file(resolve, reject);
  });
};

// 处理文件夹
const processDirectory = async (
  dirEntry: FileSystemDirectoryEntry,
  parentId?: number
) => {
  // 跳过隐藏文件夹
  if (dirEntry.name.startsWith(".")) {
    return;
  }

  const entries = await readDirectory(dirEntry);

  // 创建同名文件夹
  const { success, data } = await createFolder({
    name: dirEntry.name,
    parentId: parentId || curFolderId.value
  });

  if (success && data) {
    // 创建一个队列来存储所有上传任务
    const uploadTasks = [];

    // 递归处理文件夹内的内容
    for (const entry of entries) {
      // 跳过隐藏文件和文件夹
      if (entry.name.startsWith(".")) {
        continue;
      }

      if (entry.isFile) {
        // 将文件上传任务添加到队列
        uploadTasks.push(async () => {
          const file = await getFileFromEntry(entry as FileSystemFileEntry);
          if (file) {
            const formData = new FormData();
            formData.append("files", file); // 直接使用原始文件
            formData.append("parentId", data.id.toString());
            await uploadFile(formData);
          }
        });
      } else if (entry.isDirectory) {
        // 递归处理子文件夹
        uploadTasks.push(() =>
          processDirectory(entry as FileSystemDirectoryEntry, data.id)
        );
      }
    }

    // 按顺序执行所有上传任务
    for (const task of uploadTasks) {
      await task();
    }

    // 所有文件上传完成后，更新文件树
    const { success: treeSuccess, data: treeData } = await getFileList();
    if (treeSuccess && treeData) {
      fileTree.value = treeData;

      // 更新当前显示的文件列表
      if (!curFolderId.value) {
        fileList.value = fileTree.value;
      } else {
        const currentFolder = findCurrentFolder(
          fileTree.value,
          curFolderId.value
        );
        if (currentFolder?.children) {
          fileList.value = currentFolder.children;
        }
      }
    }

    // 只在最外层文件夹上传完成时显示提示
    if (!parentId) {
      ElMessage.success(`文件夹 ${dirEntry.name} 上传完成`);
    }
  }
};

// 读取文件夹内容
const readDirectory = (
  dirEntry: FileSystemDirectoryEntry
): Promise<FileSystemEntry[]> => {
  return new Promise((resolve, reject) => {
    const entries: FileSystemEntry[] = [];
    const reader = dirEntry.createReader();

    // 递归读取所有条目
    const readEntries = () => {
      reader.readEntries(results => {
        if (results.length) {
          entries.push(...results);
          readEntries(); // 继续读取
        } else {
          resolve(entries); // 完成读取
        }
      }, reject);
    };

    readEntries();
  });
};

// 处理双击事件
const handleDblClick = (item: FileItem) => {
  if (item.type === "folder") {
    // 进入文件夹
    curFolderId.value = item.id;
    currentPath.value.push(item.name);
    if (!item.children) item.children = [];
    fileList.value = item.children;
  } else {
    // 下载文件
    if (item.oss_url) {
      window.open(item.oss_url);
    } else {
      ElMessage.warning("文件链接不存在");
    }
  }
};

// 返回上级目录
const goBack = () => {
  if (currentPath.value.length > 1) {
    currentPath.value.pop();

    if (currentPath.value.length === 1) {
      // 返回根目录
      curFolderId.value = undefined;
      fileList.value = fileTree.value;
    } else {
      // 找到当前文件夹
      const parentFolder = findParentFolder(fileTree.value, curFolderId.value!);
      if (parentFolder) {
        // 更新当前文件夹ID和文件列表
        curFolderId.value = parentFolder.id;
        fileList.value = parentFolder.children || [];
      }
    }
  }
};

// 前进后退按钮状态
const canGoBack = computed(() => currentPath.value.length > 1);
const canGoForward = computed(() => false); // 暂时不实现前进功能

// 批量删除
const handleBatchDelete = async () => {
  if (selectedFileIds.value.length === 0) {
    ElMessage.warning("请先选择要删除的文件");
    return;
  }

  try {
    await ElMessageBox.confirm(
      `确定要删除选中的 ${selectedFileIds.value.length} 个文件吗？`,
      "提示",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }
    );

    const { success } = await deleteItems({
      ids: selectedFileIds.value
    });

    if (success) {
      const deleteIds = new Set(selectedFileIds.value);
      // 更新文件树
      if (!curFolderId.value) {
        // 从根目录删除
        fileTree.value = fileTree.value.filter(file => !deleteIds.has(file.id));
        fileList.value = fileTree.value;
      } else {
        // 从子文件夹中删除
        const parentFolder = findCurrentFolder(
          fileTree.value,
          curFolderId.value
        );
        if (parentFolder?.children) {
          parentFolder.children = parentFolder.children.filter(
            file => !deleteIds.has(file.id)
          );
          fileList.value = parentFolder.children;
        }
      }
      selectedFileIds.value = []; // 清空选择
      ElMessage.success("删除成功");
    }
  } catch (error) {
    if (error !== "cancel") {
      console.error("批量删除文件错误:", error);
      ElMessage.error("删除失败");
    }
  }
};

// 修改单个删除函数，复用批量删除的逻辑
const handleDelete = async (item: FileItem) => {
  try {
    await ElMessageBox.confirm(`确定要删除 ${item.name} 吗？`, "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning"
    });

    const { success } = await deleteItems({ ids: [item.id] });
    if (success) {
      // 更新文件树
      if (!curFolderId.value) {
        // 从根目录删除
        fileTree.value = fileTree.value.filter(file => file.id !== item.id);
        fileList.value = fileTree.value;
      } else {
        // 从子文件夹中删除
        const parentFolder = findCurrentFolder(
          fileTree.value,
          curFolderId.value
        );
        if (parentFolder?.children) {
          parentFolder.children = parentFolder.children.filter(
            file => file.id !== item.id
          );
          fileList.value = parentFolder.children;
        }
      }
      // 如果被删除的项在选中列表中，也要移除
      const index = selectedFileIds.value.indexOf(item.id);
      if (index > -1) {
        selectedFileIds.value.splice(index, 1);
      }
      ElMessage.success("删除成功");
    }
  } catch (error) {
    if (error !== "cancel") {
      console.error("删除文件错误:", error);
      ElMessage.error("删除失败");
    }
  }
};

// 选中的文件 ID 列表
const selectedFileIds = ref<number[]>([]);
const selectedItems = computed(() =>
  fileList.value.filter(item => selectedFileIds.value.includes(item.id))
);

// 修改选择处理函数
const toggleSelection = (item: FileItem, checked: boolean) => {
  const index = selectedFileIds.value.indexOf(item.id);
  if (!checked && index > -1) {
    selectedFileIds.value.splice(index, 1);
  } else if (checked && index === -1) {
    selectedFileIds.value.push(item.id);
  }
};

// 重命名文件
const handleRename = async (item: FileItem) => {
  try {
    const { value } = await ElMessageBox.prompt("请输入新名称", "重命名", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      inputValue: item.name
    });

    if (value === item.name) {
      return;
    }

    const { success } = await renameItem({
      id: item.id,
      name: value
    });

    if (success) {
      // 更新文件树
      if (!curFolderId.value) {
        // 在根目录重命名
        const file = fileTree.value.find(f => f.id === item.id);
        if (file) {
          file.name = value;
        }
        fileList.value = fileTree.value;
      } else {
        // 在子文件夹中重命名
        const parentFolder = findCurrentFolder(
          fileTree.value,
          curFolderId.value
        );
        if (parentFolder?.children) {
          const file = parentFolder.children.find(f => f.id === item.id);
          if (file) {
            file.name = value;
          }
          fileList.value = parentFolder.children;
        }
      }
      ElMessage.success("重命名成功");
    }
  } catch (error) {
    if (error !== "cancel") {
      console.error("重命名错误:", error);
      ElMessage.error("重命名失败");
    }
  }
};

// 处理修改图标
const handleUpdateIcon = async (item: FileItem) => {
  try {
    // 创建文件上传输入框
    const input = document.createElement("input");
    input.type = "file";
    input.accept = "image/*";
    input.onchange = async e => {
      const file = (e.target as HTMLInputElement).files?.[0];
      if (!file) return;

      // 检查文件大小（限制为2MB）
      if (file.size > 10 * 1024 * 1024) {
        ElMessage.error("图标大小不能超过10MB");
        return;
      }

      // 检查文件类型
      if (!file.type.startsWith("image/")) {
        ElMessage.error("请上传图片文件");
        return;
      }

      try {
        // 1. 先上传文件到OSS
        const formData = new FormData();
        formData.append("file", file);

        const { success: uploadSuccess, data: uploadData } =
          await uploadToOSS(formData);

        if (!uploadSuccess || !uploadData?.url) {
          ElMessage.error("上传图标失败");
          return;
        }

        // 2. 获取到OSS URL后，更新数据库
        const { success: updateSuccess } = await updateIcon({
          id: item.id,
          iconUrl: uploadData.url
        });

        if (updateSuccess) {
          // 3. 直接更新前端数据
          const updateItemInTree = (items: FileItem[]): boolean => {
            for (const file of items) {
              if (file.id === item.id) {
                file.icon_url = uploadData.url; // 修改为icon_url
                return true;
              }
              if (file.children?.length) {
                if (updateItemInTree(file.children)) {
                  return true;
                }
              }
            }
            return false;
          };

          // 更新文件树
          updateItemInTree(fileTree.value);

          // 更新当前显示的文件列表
          if (!curFolderId.value) {
            fileList.value = fileTree.value;
          } else {
            const currentFolder = findCurrentFolder(
              fileTree.value,
              curFolderId.value
            );
            if (currentFolder?.children) {
              fileList.value = currentFolder.children;
            }
          }

          ElMessage.success("修改图标成功");
        }
      } catch (error) {
        console.error("修改图标错误:", error);
        ElMessage.error("修改图标失败");
      }
    };
    input.click();
  } catch (error) {
    console.error("修改图标错误:", error);
    ElMessage.error("修改图标失败");
  }
};

// 处理拖拽排序
const handleSort = async (evt: any) => {
  // 如果是在同一个父级内拖动
  if (evt.from === evt.to) {
    // 检查顺序是否发生变化
    const oldIndex = evt.oldIndex;
    const newIndex = evt.newIndex;

    // 如果位置没有变化，直接返回
    if (oldIndex === newIndex) {
      return;
    }

    const items = fileList.value.map((item, index) => ({
      id: item.id,
      sortIndex: index
    }));

    try {
      const { success } = await sortItems({
        parentId: curFolderId.value,
        items
      });

      if (success) {
        // 更新文件树中的顺序
        if (!curFolderId.value) {
          fileTree.value = [...fileList.value];
        } else {
          const parentFolder = findCurrentFolder(
            fileTree.value,
            curFolderId.value
          );
          if (parentFolder) {
            parentFolder.children = [...fileList.value];
          }
        }
        ElMessage.success("排序成功");
      }
    } catch (error) {
      console.error("排序错误:", error);
      ElMessage.error("排序失败");
      // 恢复原始顺序
      evt.item.sortableInstance.sort(evt.oldIndex);
    }
  }
};

// 初始化加载
const loadFileTree = async () => {
  try {
    const { success, data } = await getFileList();
    if (success && data) {
      fileTree.value = data;
      // 如果不在子文件夹中，显示根目录文件
      if (!curFolderId.value) {
        fileList.value = fileTree.value;
      }
    }
  } catch (error) {
    console.error("获取文件列表错误:", error);
    ElMessage.error("获取文件列表失败");
  }
};

// 在组件挂载时加载文件树
onMounted(() => {
  loadFileTree();
});
</script>

<template>
  <div
    class="file-manager"
    @drop.prevent="handleDrop"
    @dragover.prevent
    @dragenter.prevent="handleDragEnter"
    @dragleave.prevent="handleDragLeave"
  >
    <!-- 顶部工具栏 -->
    <div class="top-toolbar">
      <div class="left-actions">
        <div class="nav-buttons">
          <el-button-group>
            <el-button :icon="ArrowLeft" :disabled="!canGoBack" @click="goBack">
              后退
            </el-button>
            <el-button :icon="ArrowRight" :disabled="!canGoForward">
              前进
            </el-button>
          </el-button-group>
        </div>
        <span class="path">{{ currentPath.join(" / ") }}</span>
      </div>
      <div class="right-actions">
        <el-input
          v-model="searchKeyword"
          placeholder="搜索"
          :prefix-icon="Search"
          class="search-input"
        />
        <el-button type="primary" plain @click="createNewFolder">
          新建文件夹
        </el-button>
        <el-upload
          :auto-upload="true"
          :show-file-list="false"
          :multiple="true"
          :http-request="options => handleUpload(options.file)"
        >
          <el-button plain>上传文件</el-button>
        </el-upload>
        <el-button
          type="danger"
          plain
          :disabled="selectedFileIds.length === 0"
          @click="handleBatchDelete"
        >
          删除选中项 ({{ selectedFileIds.length }})
        </el-button>
        <el-button plain>列表视图</el-button>
      </div>
    </div>

    <!-- 文件网格，使用 draggable 包装 -->
    <div>
      <draggable
        :list="fileList"
        item-key="id"
        class="file-grid-items"
        ghost-class="ghost"
        @end="handleSort"
      >
        <template #item="{ element: item }">
          <div class="file-item" @dblclick="handleDblClick(item)">
            <el-checkbox
              :model-value="selectedFileIds.includes(item.id)"
              class="file-checkbox"
              @click.stop
              @update:model-value="checked => toggleSelection(item, checked)"
            />
            <div class="file-icon">
              <template v-if="item.icon_url">
                <img :src="item.icon_url" alt="icon" class="custom-icon" />
              </template>
              <template v-else>
                <el-icon :size="32">
                  <component :is="getFileIcon(item.mime_type)" />
                </el-icon>
              </template>
            </div>
            <div class="file-name">{{ item.name }}</div>
            <div class="file-actions">
              <el-dropdown trigger="hover" placement="bottom-end">
                <span class="action-trigger">
                  <el-icon><More /></el-icon>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item @click="handleRename(item)">
                      <el-icon><Edit /></el-icon>重命名
                    </el-dropdown-item>
                    <el-dropdown-item @click="handleUpdateIcon(item)">
                      <el-icon><Picture /></el-icon>修改图标
                    </el-dropdown-item>
                    <el-dropdown-item
                      v-if="item.type === 'file'"
                      @click="handleDblClick(item)"
                    >
                      <el-icon><Download /></el-icon>下载
                    </el-dropdown-item>
                    <el-dropdown-item
                      divided
                      type="danger"
                      @click="handleDelete(item)"
                    >
                      <el-icon><Delete /></el-icon>删除
                    </el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </div>
        </template>
      </draggable>
    </div>

    <!-- 拖拽上传提示 -->
    <div class="drag-overlay" :class="{ active: isDragging }">
      <el-icon :size="48"><Upload /></el-icon>
      <span>释放鼠标上传文件或文件夹</span>
    </div>
  </div>
</template>

<style scoped>
.file-manager {
  padding: 16px;
  min-height: 100%;
  background-color: #fff;
  position: relative;
  overflow: hidden;
}

.top-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 0;
  margin-bottom: 16px;
}

.left-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}

.right-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}

.nav-buttons {
  display: flex;
  align-items: center;
}

.nav-buttons :deep(.el-button) {
  height: 32px;
  padding: 8px 12px;
}

.path {
  font-size: 14px;
  color: #606266;
  margin-left: 12px;
}

.search-input {
  width: 240px;
}

.file-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 16px;
  position: relative;
  min-height: 200px;
  padding: 16px;
  background-color: #f6f8fa;
  border-radius: 8px;
}

.file-grid::after {
  content: "拖拽文件到此处上传";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #909399;
  font-size: 14px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s;
}

.file-grid:empty::after {
  opacity: 1;
}

.file-item {
  position: relative;
  padding: 8px;
  cursor: pointer;
  border-radius: 4px;
  transition: all 0.2s;
  text-align: center;
  background-color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.file-item:hover {
  background-color: #f5f7fa;
  transform: translateY(-2px);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

.file-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 4px;
  font-size: 48px;
}

.file-name {
  font-size: 13px;
  color: #606266;
  text-align: center;
  word-break: break-all;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  width: 100%;
  padding: 0 4px;
}

.file-checkbox {
  position: absolute;
  top: 4px;
  left: 4px;
  display: none;
  z-index: 1;
}

.file-item:hover .file-checkbox,
.file-checkbox.is-checked {
  display: block;
}

.file-actions {
  position: absolute;
  top: 4px;
  right: 4px;
  display: none;
}

.file-item:hover .file-actions {
  display: block;
}

.action-trigger {
  cursor: pointer;
  padding: 2px;
  border-radius: 2px;
  color: #606266;
}

.action-trigger:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

:deep(.el-dropdown-menu__item) {
  display: flex;
  align-items: center;
  gap: 8px;
}

:deep(.el-button--text) {
  padding: 4px 8px;
}

.file-grid-items {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 16px;
  width: 100%;
}

.ghost {
  opacity: 0.5;
  background: #c8ebfb;
}

/* 拖拽上传遮罩 */
.drag-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(64, 158, 255, 0.1);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  color: var(--el-color-primary);
  font-size: 16px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s;
  z-index: 10;
}

.drag-overlay.active {
  opacity: 1;
}

.custom-icon {
  width: 48px;
  height: 48px;
  object-fit: contain;
}
</style>
